package wavRead;

import FFT.*;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by 77430 on 2016/11/4.
 */
public class WavFileReader {
    static private int lenchunkdescriptor = 4;
    static private int lenchunksize = 4;
    static private int lenwaveflag = 4;
    static private int lenfmtubchunk = 4;
    static private int lensubchunk1size = 4;
    static private int lenaudioformat = 2;
    static private int lennumchannels = 2;
    static private int lensamplerate = 2;
    static private int lenbyterate = 4;
    static private int lenblockling = 2;
    static private int lenbitspersample = 2;
    static private int lenextraParamSize = 2;
    static private int lendatasubchunk = 4;
    static private int lensubchunk2size = 4;
    private String filename = null;//批量读取不用 单个读取 可以使用
    private double[] data = null;//数据  即音频的时域
    private int len = 0;
    private String chunkdescriptor = null;//根结点名称 RIFF
    private long chunksize = 0;//长度
    private String waveflag = null;//文件格式 wave
    private String fmtubchunk = null;
    private long subchunk1size = 0;//格式结点长度
    private int audioformat = 0;//音频格式 1为pcm
    private int numchannels = 0;//声道数 单声道为1
    private long samplerate = 0;//采样率
    private long byterate = 0;// 比特率/8
    private int blockalign = 0;
    private int bitspersample = 0;//位深度 16
    private long extraParamSize = 0;//可能出现的其他参数长度
    private String datasubchunk = null;//数据结点的名称 data
    private long subchunk2size = 0;//数据结点的长度
    private FileInputStream fis = null;
    private BufferedInputStream bis = null;


    public WavFileReader(File file) {

        this.initReader(file);
    }


    // 获取每个采样的编码长度，8bit或者16bit
    public int getBitPerSample() {
        return this.bitspersample;
    }

    // 获取采样率
    public long getSampleRate() {
        return this.samplerate;
    }

    // 获取声道个数，1代表单声道 2代表立体声
    public int getNumChannels() {
        return this.numchannels;
    }

    // 获取数据长度，也就是一共采样多少个
    public int getDataLen() {
        return this.len;
    }

    // 获取数据
    // 数据是一个double数组，从byte翻译为short再翻译为double
    public double[] getData() {
        return this.data;
    }

    private void initReader(File file) {

        try {
            fis = new FileInputStream(file);
            bis = new BufferedInputStream(fis);

            this.chunkdescriptor = readString(lenchunkdescriptor);

            if (!chunkdescriptor.endsWith("RIFF"))
                throw new IllegalArgumentException("RIFF miss, " + filename + " is not a wave file.");

            //System.out.println("格式是" + chunkdescriptor);

            this.chunksize = readLong();
            this.waveflag = readString(lenwaveflag);
            if (!waveflag.endsWith("WAVE"))
                throw new IllegalArgumentException("WAVE miss, " + filename + " is not a wave file.");

            this.fmtubchunk = readString(lenfmtubchunk);
            if (!fmtubchunk.endsWith("fmt "))
                throw new IllegalArgumentException("fmt miss, " + filename + " is not a wave file.");

            this.subchunk1size = readLong();

            this.audioformat = readInt();
            /*if (audioformat == 1)
                System.out.println("是pcm格式");*/

            this.numchannels = readInt();
            /*if (numchannels == 1)
                System.out.println("是单声道");*/

            this.samplerate = readLong();
            /*if (samplerate == 44100l)
                System.out.println("采样率是44100HZ");*/

            this.byterate = readLong();
            this.blockalign = readInt();
            this.bitspersample = readInt();

            /*if (bitspersample == 16)
                System.out.println("位深度是16");*/

            // 可能出现的其他参数
            if (subchunk1size != 16) {
                this.extraParamSize = readInt();
                //纯pcm没有  这里只读取长度就不做处理了 au剪辑的wav里是没有extraParamSize的
            }
            this.datasubchunk = readString(lendatasubchunk);
            if (!datasubchunk.endsWith("data"))
                throw new IllegalArgumentException("data miss, " + filename + " is not a wave file.");
            this.subchunk2size = readLong();


            this.len = (int) (this.subchunk2size / (this.bitspersample / 8) / this.numchannels);

            this.data = new double[this.len];

            for (int i = 0; i < this.len; ++i) {
                if (this.bitspersample == 8) {
                    this.data[i] = bis.read();
                } else if (this.bitspersample == 16) {
                    this.data[i] = this.readShortTodouble();
                }
            }


        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bis != null)
                    bis.close();
                if (fis != null)
                    fis.close();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
    }

    private String readString(int len) {
        byte[] buf = new byte[len];
        try {
            if (bis.read(buf) != len)
                throw new IOException("no more data!!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(buf);
    }

    private int readInt() {
        byte[] buf = new byte[2];
        int res = 0;
        try {
            if (bis.read(buf) != 2)
                throw new IOException("no more data!!!");
            res = (buf[0] & 0x000000FF) | (((int) buf[1]) << 8);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    private double readShortTodouble() {
        byte[] buf = new byte[2];
        short res = 0;
        try {
            if (bis.read(buf) != 2)
                throw new IOException("no more data!!");
            res = (short) ((buf[1] & 0x00FF) << 8 | (buf[0] & 0x00FF));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return (double) res;
        //return res / 32768f;//32768= 2^15 // 和matlab对齐
    }

    private long readLong() {
        long res = 0;
        try {
            long[] l = new long[4];
            for (int i = 0; i < 4; ++i) {
                l[i] = bis.read();
                if (l[i] == -1) {
                    throw new IOException("no more data!!!");
                }
            }
            res = l[0] | (l[1] << 8) | (l[2] << 16) | (l[3] << 24);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return res;
    }

    private byte[] readBytes(int len) {
        byte[] buf = new byte[len];
        try {
            if (bis.read(buf) != len)
                throw new IOException("no more data!!!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return buf;
    }


    //截取一帧  存到list中  效率一般
    public static Object[] splitAry(double[] ary, int subSize) {
        int count = ary.length % subSize == 0 ? ary.length / subSize: ary.length / subSize + 1;

        List<List<Double>> subAryList = new ArrayList<List<Double>>();

        for (int i = 0; i < count; i++) {
            int index = i * subSize;
            List<Double> list = new ArrayList<Double>();
            int j = 0;
            while (j < subSize && index < ary.length) {
                list.add(ary[index++]);
                j++;
            }
            while(j<subSize){
                list.add(0.000);
                j++;
            }
            subAryList.add(list);
        }

        Object[] subAry = new Object[subAryList.size()];

        for(int i = 0; i < subAryList.size(); i++){
            List<Double> subList = subAryList.get(i);
            double[] subAryItem = new double[subList.size()];
            for(int j = 0; j < subList.size(); j++){
                subAryItem[j] = subList.get(j).intValue();
            }

            subAry[i] = subAryItem;
            subAryItem = null;
        }

        return subAry;
    }
}

